#ifndef GTREE_H
#define GTREE_H

#include "gTreeNode.h"

template<class E>
class GTree
{
public:
    GTree()
    {
        this->root_ = 0;
    }

    GTree(const E& rootElement)
    {
        this->root_ = new GTreeNode<E>(rootElement);
    }

    GTree(const GTree<E>& orig)
    {
        this->destroy(this->root_);
        this->root_ = orig.root_;
    }

    ~GTree()
    {
        this->destroy(&this->root_);
    }

    int size()
    {
        return !this->root_ ? 0 : this->root_->size();
    }

    GTreeNode<E>& search(const E& element)
    {
        if(!this->root_)
            throw NonInitializedReference();
        else
        {
            if(this->root_->element_ == element)
                return *this->root_;

            GTreeNode<E> *found = this->root_->search(element);
            if(found)
                return *found;
            else
                throw NonInitializedReference();
        }
    }

    GTreeNode<E>& getRoot()
    {
        if(this->root_ == 0)
            throw NonInitializedReference();
        return *this->root_;
    }

    void setRoot(GTreeNode<E> *rootNode)
    {
        destroy(&this->root_);
        this->root_ = rootNode;
    }

    void insert(const E& parent, const E& element)
    {
        GTreeNode<E>& parentNode= search(parent);
        parentNode.add(element);
    }

    void remove(const E& element)
    {
        if(this->root_ && element == this->root_->element_)
            this->destroy(&this->root_);
        else
            this->root_->remove(element);
    }

    void traverse()
    {
        int level = 0;
        if(this->root_)
            this->root_->traverse(level);
    }

private:
    void destroy(GTreeNode<E> **node)
    {
        if(*node)
        {
            delete *node;
            *node = 0;
        }
    }

private:
    GTreeNode<E> *root_;
};

#endif // GTREE_H

